﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Diagnostics.Contracts;
using Shweet.Repository.Exceptions;

namespace Shweet.Repository
{
    public class ShweetMessages : IShweetMessages
    {
        public void Add(Shweet shweet)
        {
            SPList shweetList = GetShweetsList();
            SPListItem newShweet = shweetList.AddItem();
            newShweet["ShweetText"] = shweet.Text;
            InsertTags(shweet, newShweet);
            newShweet["User"] = shweet.User;
            newShweet["ShweetedOn"] = DateTime.Now;
            newShweet.Update();
        }

        private void InsertTags(Shweet shweet, SPListItem newShweet)
        {
            for (int i = 0; i < 5; i++)
            {
                try
                {
                    newShweet[string.Format("Tag0{0}", (i + 1).ToString())] = shweet.Tags[i];
                }
                catch (IndexOutOfRangeException)
                {
                    newShweet[string.Format("Tag0{0}", (i + 1).ToString())] = null;
                }
            }
        }

        /// <summary>
        /// Load using Linq Query that will perform a filtered CAML query
        /// </summary>
        /// <param name="tags"></param>
        /// <returns></returns>
        public Shweet[] Load(string[] tags)
        {
            return Load(tags, SPContext.Current.Web.Url);
        }

        /// <summary>
        /// Load using Linq query that will perform a filtered CAML query based on the team site url provided.
        /// </summary>
        /// <param name="tags"></param>
        /// <param name="shweetTeamSiteUrl"></param>
        /// <returns></returns>
        public Shweet[] Load(string[] tags, string shweetTeamSiteUrl)
        {
            var shweets = new List<Shweet>();

            using (var dataContext = new ShweetsDataContext(shweetTeamSiteUrl))
            {
                var sb = new StringBuilder();
                TextWriter tw = new StringWriter(sb);
                dataContext.Log = tw;

                IQueryable<ShweetsItem> query;
                if (tags.Length == 0)
                {
                    query = (from s in dataContext.Shweets
                             orderby s.ShweetedOn descending
                             select s).Take(30);
                }
                else
                {
                    var fiveTags = new string[5];
                    tags.CopyTo(fiveTags, 0);
                    tags = fiveTags;

                    query = (from s in dataContext.Shweets
                             where
                                 s.Tag01 == tags[0] || s.Tag01 == tags[1] || s.Tag01 == tags[2] || s.Tag01 == tags[3] || s.Tag01 == tags[4] ||
                                 s.Tag02 == tags[0] || s.Tag02 == tags[1] || s.Tag02 == tags[2] || s.Tag02 == tags[3] || s.Tag02 == tags[4] ||
                                 s.Tag03 == tags[0] || s.Tag03 == tags[1] || s.Tag03 == tags[2] || s.Tag03 == tags[3] || s.Tag03 == tags[4] ||
                                 s.Tag04 == tags[0] || s.Tag04 == tags[1] || s.Tag04 == tags[2] || s.Tag04 == tags[3] || s.Tag04 == tags[4] ||
                                 s.Tag05 == tags[0] || s.Tag05 == tags[1] || s.Tag05 == tags[2] || s.Tag05 == tags[3] || s.Tag05 == tags[4]
                             orderby s.ShweetedOn descending
                             select s).Take(30);


                }
                ParseResults(query, shweets);
            }

            return shweets.ToArray();
        }


        /// <summary>
        /// Load using Linq to SharePoint that uses two phase process as it is unable to map the Linq
        /// expression directlty to CAML.
        /// 
        /// WARNING: The query returns ALL items from the list (subject to query throttling) and filters these 
        /// afterwards
        /// </summary>
        /// <param name="tags"></param>
        /// <returns></returns>
        public Shweet[] LoadTwoPhase(string[] tags)
        {
            Func<string[], bool> filterTags = s =>
            {
                if (tags.Length == 0)
                    return true;
                return tags.Intersect(s).Count() > 0;
            };

            List<Shweet> shweets = new List<Shweet>();
            using (var dataContext = new ShweetsDataContext(SPContext.Current.Web.Url))
            {
                //Used to debug the CAML generated
                StringBuilder sb = new StringBuilder();
                TextWriter tw = new StringWriter(sb);
                dataContext.Log = tw;

                //  This is the two phase query this is much more in the style of Linq
                var twoPhasequery = (from s in dataContext.Shweets
                                     orderby s.ShweetedOn descending
                                     where filterTags(new string[] { s.Tag01, s.Tag02, s.Tag03, s.Tag04, s.Tag05 })
                                     select s).Take(30);
                ParseResults(twoPhasequery, shweets);
            }
            return shweets.ToArray();
        }

        /// <summary>
        /// Load using manual CAML query
        /// The query is the same as generated by the <see cref="Load"/> but cannot be unit tested
        /// </summary>
        /// <param name="tags"></param>
        /// <returns></returns>
        public Shweet[] LoadQuery(string[] tags)
        {
            var query = new SPQuery();
            query.Query = GetCaml(tags);
            query.RowLimit = 30;

            SPList shweetList = GetShweetsList();
            SPListItemCollection results = shweetList.GetItems(query);
            var shweets = new List<Shweet>();

            foreach (SPListItem item in results)
            {
                var shweet = new Shweet()
                {
                    Text = item["ShweetText"].ToString(),
                    User = item["User"].ToString(),
                    Tags = new string[]
                        {
                            item["Tag01"] != null ? item["Tag01"].ToString() : string.Empty,
                            item["Tag02"] != null ? item["Tag02"].ToString() : string.Empty,
                            item["Tag03"] != null ? item["Tag03"].ToString() : string.Empty,
                            item["Tag04"] != null ? item["Tag04"].ToString() : string.Empty,
                            item["Tag05"] != null ? item["Tag05"].ToString() : string.Empty
                        }
                };
                shweets.Add(shweet);
            }
            return shweets.ToArray();
        }

        private void ParseResults(IQueryable<ShweetsItem> query, List<Shweet> shweets)
        {
            shweets.AddRange(query.Select(item => new Shweet()
            {
                User = item.User,
                Text = item.Title,
                ShweetedOn = (DateTime)item.ShweetedOn,
                Tags = new string[]
                {
                    item.Tag01, item.Tag02, item.Tag03, item.Tag04, item.Tag05
                }
            }));
        }

        private static SPList GetShweetsList()
        {
            SPContext context = SPContext.Current;
            SPWeb web = context.Web;
            SPList shweetsList = web.Lists.TryGetList("Shweets");
            if (shweetsList == null)
            {
                throw new DataStoreNotFoundException("Shweet list not found in current site.");
            }
            return shweetsList;
        }


        private string GetCaml(string[] tags)
        {

            StringBuilder sb = new StringBuilder();
            sb.Append("<View>");
            sb.Append("  <Query>");
            sb.Append("    <Where>");
            sb.Append("      <And>");
            sb.Append("        <BeginsWith>");
            sb.Append("          <FieldRef Name=\"ContentTypeId\" />");
            sb.Append("          <Value Type=\"ContentTypeId\">0x0100</Value>");
            sb.Append("        </BeginsWith>");
            sb.Append("        <Or>");
            sb.Append("          <Or>");
            sb.Append("            <Or>");
            sb.Append("              <Or>");
            sb.Append("                <Or>");
            sb.Append("                  <Or>");
            sb.Append("                    <Or>");
            sb.Append("                      <Or>");
            sb.Append("                        <Or>");
            sb.Append("                          <Or>");
            sb.Append("                            <Or>");
            sb.Append("                              <Or>");
            sb.Append("                                <Or>");
            sb.Append("                                  <Or>");
            sb.Append("                                    <Or>");
            sb.Append("                                      <Or>");
            sb.Append("                                        <Or>");
            sb.Append("                                          <Or>");
            sb.Append("                                            <Or>");
            sb.Append("                                              <Or>");
            sb.Append("                                                <Or>");
            sb.Append("                                                  <Or>");
            sb.Append("                                                    <Or>");
            sb.Append("                                                      <Or>");
            sb.Append("                                                        <Eq>");
            sb.Append("                                                          <FieldRef Name=\"_x0054_ag01\" />");
            sb.AppendFormat("                                                          <Value Type=\"Text\">{0}</Value>", tags[0]);
            sb.Append("                                                        </Eq>");
            sb.Append("                                                        <Eq>");
            sb.Append("                                                          <FieldRef Name=\"_x0054_ag01\" />");
            sb.AppendFormat("                                                          <Value Type=\"Text\">{0}</Value>", tags[1]);
            sb.Append("                                                        </Eq>");
            sb.Append("                                                      </Or>");
            sb.Append("                                                      <Eq>");
            sb.Append("                                                        <FieldRef Name=\"_x0054_ag01\" />");
            sb.AppendFormat("                                                        <Value Type=\"Text\">{0}</Value>", tags[2]);
            sb.Append("                                                      </Eq>");
            sb.Append("                                                    </Or>");
            sb.Append("                                                    <Eq>");
            sb.Append("                                                      <FieldRef Name=\"_x0054_ag01\" />");
            sb.AppendFormat("                                                      <Value Type=\"Text\">{0}</Value>", tags[3]);
            sb.Append("                                                    </Eq>");
            sb.Append("                                                  </Or>");
            sb.Append("                                                  <Eq>");
            sb.Append("                                                    <FieldRef Name=\"_x0054_ag01\" />");
            sb.AppendFormat("                                                    <Value Type=\"Text\">{0}</Value>", tags[4]);
            sb.Append("                                                  </Eq>");
            sb.Append("                                                </Or>");
            sb.Append("                                                <Eq>");
            sb.Append("                                                  <FieldRef Name=\"_x0054_ag02\" />");
            sb.AppendFormat("                                                  <Value Type=\"Text\">Tag</Value>", tags[0]);
            sb.Append("                                                </Eq>");
            sb.Append("                                              </Or>");
            sb.Append("                                              <Eq>");
            sb.Append("                                                <FieldRef Name=\"_x0054_ag02\" />");
            sb.AppendFormat("                                                <Value Type=\"Text\">{0}</Value>", tags[1]);
            sb.Append("                                              </Eq>");
            sb.Append("                                            </Or>");
            sb.Append("                                            <Eq>");
            sb.Append("                                              <FieldRef Name=\"_x0054_ag02\" />");
            sb.AppendFormat("                                              <Value Type=\"Text\">{0}</Value>", tags[2]);
            sb.Append("                                            </Eq>");
            sb.Append("                                          </Or>");
            sb.Append("                                          <Eq>");
            sb.Append("                                            <FieldRef Name=\"_x0054_ag02\" />");
            sb.AppendFormat("                                            <Value Type=\"Text\">{0}</Value>", tags[3]);
            sb.Append("                                          </Eq>");
            sb.Append("                                        </Or>");
            sb.Append("                                        <Eq>");
            sb.Append("                                          <FieldRef Name=\"_x0054_ag02\" />");
            sb.AppendFormat("                                          <Value Type=\"Text\">{0}</Value>", tags[4]);
            sb.Append("                                        </Eq>");
            sb.Append("                                      </Or>");
            sb.Append("                                      <Eq>");
            sb.Append("                                        <FieldRef Name=\"_x0054_ag03\" />");
            sb.AppendFormat("                                        <Value Type=\"Text\">{0}</Value>", tags[0]);
            sb.Append("                                      </Eq>");
            sb.Append("                                    </Or>");
            sb.Append("                                    <Eq>");
            sb.Append("                                      <FieldRef Name=\"_x0054_ag03\" />");
            sb.AppendFormat("                                      <Value Type=\"Text\">{0}</Value>", tags[1]);
            sb.Append("                                    </Eq>");
            sb.Append("                                  </Or>");
            sb.Append("                                  <Eq>");
            sb.Append("                                    <FieldRef Name=\"_x0054_ag03\" />");
            sb.AppendFormat("                                    <Value Type=\"Text\">{0}</Value>", tags[2]);
            sb.Append("                                  </Eq>");
            sb.Append("                                </Or>");
            sb.Append("                                <Eq>");
            sb.Append("                                  <FieldRef Name=\"_x0054_ag03\" />");
            sb.AppendFormat("                                  <Value Type=\"Text\">{0}</Value>", tags[3]);
            sb.Append("                                </Eq>");
            sb.Append("                              </Or>");
            sb.Append("                              <Eq>");
            sb.Append("                                <FieldRef Name=\"_x0054_ag03\" />");
            sb.AppendFormat("                                <Value Type=\"Text\">{0}</Value>", tags[4]);
            sb.Append("                              </Eq>");
            sb.Append("                            </Or>");
            sb.Append("                            <Eq>");
            sb.Append("                              <FieldRef Name=\"_x0054_ag04\" />");
            sb.AppendFormat("                              <Value Type=\"Text\">{0}</Value>", tags[0]);
            sb.Append("                            </Eq>");
            sb.Append("                          </Or>");
            sb.Append("                          <Eq>");
            sb.Append("                            <FieldRef Name=\"_x0054_ag04\" />");
            sb.AppendFormat("                            <Value Type=\"Text\">{0}</Value>", tags[1]);
            sb.Append("                          </Eq>");
            sb.Append("                        </Or>");
            sb.Append("                        <Eq>");
            sb.Append("                          <FieldRef Name=\"_x0054_ag04\" />");
            sb.AppendFormat("                          <Value Type=\"Text\">{0}</Value>", tags[2]);
            sb.Append("                        </Eq>");
            sb.Append("                      </Or>");
            sb.Append("                      <Eq>");
            sb.Append("                        <FieldRef Name=\"_x0054_ag04\" />");
            sb.AppendFormat("                        <Value Type=\"Text\">{0}</Value>", tags[3]);
            sb.Append("                      </Eq>");
            sb.Append("                    </Or>");
            sb.Append("                    <Eq>");
            sb.Append("                      <FieldRef Name=\"_x0054_ag04\" />");
            sb.AppendFormat("                      <Value Type=\"Text\">{0}</Value>", tags[4]);
            sb.Append("                    </Eq>");
            sb.Append("                  </Or>");
            sb.Append("                  <Eq>");
            sb.Append("                    <FieldRef Name=\"_x0054_ag05\" />");
            sb.AppendFormat("                    <Value Type=\"Text\">{0}</Value>", tags[0]);
            sb.Append("                  </Eq>");
            sb.Append("                </Or>");
            sb.Append("                <Eq>");
            sb.Append("                  <FieldRef Name=\"_x0054_ag05\" />");
            sb.AppendFormat("                  <Value Type=\"Text\">{0}</Value>", tags[1]);
            sb.Append("                </Eq>");
            sb.Append("              </Or>");
            sb.Append("              <Eq>");
            sb.Append("                <FieldRef Name=\"_x0054_ag05\" />");
            sb.AppendFormat("                <Value Type=\"Text\">{0}</Value>", tags[2]);
            sb.Append("              </Eq>");
            sb.Append("            </Or>");
            sb.Append("            <Eq>");
            sb.Append("              <FieldRef Name=\"_x0054_ag05\" />");
            sb.AppendFormat("              <Value Type=\"Text\">{0}</Value>", tags[3]);
            sb.Append("            </Eq>");
            sb.Append("          </Or>");
            sb.Append("          <Eq>");
            sb.Append("            <FieldRef Name=\"_x0054_ag05\" />");
            sb.AppendFormat("            <Value Type=\"Text\"></Value>", tags[4]);
            sb.Append("          </Eq>");
            sb.Append("        </Or>");
            sb.Append("      </And>");
            sb.Append("    </Where>");
            sb.Append("    <OrderBy Override=\"TRUE\">");
            sb.Append("      <FieldRef Name=\"ShweetedOn\" Ascending=\"FALSE\" />");
            sb.Append("    </OrderBy>");
            sb.Append("  </Query>");
            sb.Append("  <ViewFields>");
            sb.Append("    <FieldRef Name=\"_x0054_ag01\" />");
            sb.Append("    <FieldRef Name=\"_x0054_ag02\" />");
            sb.Append("    <FieldRef Name=\"_x0054_ag03\" />");
            sb.Append("    <FieldRef Name=\"_x0054_ag04\" />");
            sb.Append("    <FieldRef Name=\"_x0054_ag05\" />");
            sb.Append("    <FieldRef Name=\"User\" />");
            sb.Append("    <FieldRef Name=\"ShweetedOn\" />");
            sb.Append("    <FieldRef Name=\"ID\" />");
            sb.Append("    <FieldRef Name=\"owshiddenversion\" />");
            sb.Append("    <FieldRef Name=\"FileDirRef\" />");
            sb.Append("    <FieldRef Name=\"Title\" />");
            sb.Append("</ViewFields>");
            sb.Append("  </ViewFields>");
            sb.Append("  <RowLimit Paged=\"TRUE\">30</RowLimit>");
            sb.Append("</View>");

            return sb.ToString();
        }
    }
}


